home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 8: LINUX Games / Linux Cubed Series 8 - LINUX Games.iso / games / x11 / networke / civ-0.000 / civ-0 / civ-0.3 / src / city.cc.orig < prev    next >
Text File  |  1995-11-19  |  12KB  |  468 lines

  1. #include <string.h>
  2.  
  3. #include "misc.h"
  4. #include "defs.h"
  5. #include "graph.h"
  6. #include "city.h"
  7. #include "world.h"
  8. #include "rivers.h"
  9. #include "MsgQ.h"
  10. #include "player.h"
  11. #include "units.h"
  12. #include "trans.h"
  13. #include "display.h"
  14. #include "move.h"
  15. #include "rules.h"
  16. #include "hash.h"
  17. #include "savefile.h"
  18.  
  19. #ifdef FOR_X11
  20. #include <sys/types.h>
  21. #include <netinet/in.h>
  22. #else
  23. #include <winsock.h>
  24. #endif
  25.  
  26. // pixmaps
  27. #include "../pic/bulb.xpm"
  28. #include "../pic/elvis.xpm"
  29. #include "../pic/food.xpm"
  30. #include "../pic/goonda.xpm"
  31. #include "../pic/lux.xpm"
  32. #include "../pic/money.xpm"
  33. #include "../pic/shield.xpm"
  34. #include "../pic/trade.xpm"
  35. #include "../pic/worker.xpm"
  36.  
  37. #include "../pic/city.xpm"
  38.  
  39. const char *backGroundColor = "#79e78e38ffff";
  40.  
  41. int City::white, City::black, City::back, City::red, City::gray;
  42.  
  43. long City::hBulb, City::hElvis, City::hFood;
  44. long City::hGoonda, City::hLux, City::hMoney;
  45. long City::hShield, City::hTrade, City::hWorker;
  46.  
  47. City::City(char *Name, ulong Id, int X, int Y, uchar OwnerId)
  48. {
  49.   name = Name;
  50.   x = X;
  51.   y = Y;
  52.   id = Id;
  53.   trans->SetPtr(id, this);
  54.   ownerId = OwnerId;
  55.   players[ownerId]->citys.Insert(id);
  56.   size = 1;
  57.  
  58.   building = "Settlers";
  59.   accProd = 0;
  60.   needProd = buildObjects.Find(StrKey(building))->prodCost;
  61.  
  62.   foodSupport = foodStored = 0;
  63.   foodStoreCap = 20;
  64.  
  65.   world->WhichCity(x,y) = id;
  66.   if (ownerId == playerId) world->SeeSquare(x, y, 1);
  67.   world->Working(x, y) = id;
  68.   DrawIfVisible();
  69.   PutExtraWorker();
  70.   if (ownerId == playerId)
  71.     ComputeEcon();
  72. }
  73.  
  74. // read from save file
  75. City::City()
  76. {
  77.   id = ReadULong();
  78.   name = ReadString();
  79.   x = ReadUShort();
  80.   y = ReadUShort();
  81.   ownerId = ReadUChar();
  82.   size = ReadUChar();
  83.   building = LookupBuilding(ReadString(), 0);
  84.   accProd = ReadUShort();
  85.   needProd = ReadUShort();
  86.   foodStored = ReadUShort();
  87.   foodSupport = ReadUShort();
  88.   foodStoreCap = ReadUShort();
  89.   int n = ReadUShort();
  90.   for (;n > 0; --n)
  91.     buildings.Insert(LookupBuilding(ReadString(), 0));
  92.  
  93.   trans->SetPtr(id, this);
  94.   players[ownerId]->citys.Insert(id);
  95.   world->WhichCity(x, y) = id;
  96. }
  97.  
  98. City::~City()
  99. {
  100.   for (int xi = 0; xi < 5; ++xi) for (int yi = 0; yi < 5; ++yi) {
  101.     if ((xi % 4) == 0 && (yi % 4) == 0) continue;
  102.     int x1 = world->FixX(x+xi-2), y1 = world->FixY(y+yi-2);
  103.     if (world->Working(x1, y1) == id)
  104.       world->Working(x1, y1) = 0;
  105.   }
  106.   while (buildings) {
  107.     char *name = buildings.RemoveHead();
  108.     players[ownerId]->DeleteWonder(name);
  109.   }
  110.   world->WhichCity(x,y) = 0;
  111.   players[ownerId]->citys.Delete(id);
  112.   delete name;
  113.   DrawIfVisible();
  114.   if (id == playerId)
  115.     world->HideSquare(x, y, 2);
  116. }
  117.  
  118. char *City::LookupBuilding(char *str, int insertWonder)
  119. {
  120.   BuildObject *obj = buildObjects.Find(StrKey(str));
  121.   delete str;
  122.   if (obj == NULL)
  123.     return NULL;
  124.   if (insertWonder && obj->wonder) {
  125.     players[ownerId]->wonders.Insert(obj->name);
  126.     obj->built = 1;
  127.   }
  128.   return obj->name;
  129. }
  130.  
  131. void City::DeleteBuilding(char *name)
  132. {
  133.   for (Lister<charp> l = buildings; l; l.Next())
  134.     if (strcmp(l.Elem(), name) == 0) {
  135.       l.Delete();
  136.       return;
  137.     }
  138. }
  139.  
  140. void City::Draw(int atx, int aty)
  141. {
  142.   if (players[ownerId]->cityPic == 0)
  143.     players[ownerId]->cityPic = players[ownerId]->CompilePic(city_xpm);
  144.   screen->DrawPixmap(atx, aty, players[ownerId]->cityPic);
  145.   if (size < 10)
  146.     screen->WriteInt(atx+4, aty+4, size, 1, black);
  147.   else
  148.     screen->WriteInt(atx, aty, size, 2, black);
  149.   if (HasBuilding("City Walls")) {
  150.     int w, h;
  151.     screen->GetPixmapInfo(players[ownerId]->cityPic, w, h);
  152.     screen->Rect(atx+1, aty+1, w-2, h-2, gray);
  153.     screen->Rect(atx, aty, w, h, gray);
  154.   }
  155. }
  156.  
  157. void City::DrawIfVisible()
  158. {
  159.   if (!world->Visible(x, y) || !display->Visible(x, y)) return;
  160.   int sx, sy;
  161.   display->TranslateScreen(x, y, sx, sy);
  162.   world->Draw(x, y, 1, 1, sx, sy);
  163. }
  164.  
  165. void AllocCityColors()
  166. {
  167.   City::white = screen->AllocColor("#ffffffffffff");
  168.   City::red = screen->AllocColor("red");
  169.   City::black = screen->AllocColor("#000000000000");
  170.   City::back = screen->AllocColor((char *)backGroundColor);
  171.   City::gray = screen->AllocColor("SlateGray");
  172.   City::hBulb = screen->CompilePixmap(bulb_xpm);
  173.   City::hElvis = screen->CompilePixmap(elvis_xpm);
  174.   City::hFood = screen->CompilePixmap(food_xpm);
  175.   City::hGoonda = screen->CompilePixmap(goonda_xpm);
  176.   City::hLux = screen->CompilePixmap(lux_xpm);
  177.   City::hMoney = screen->CompilePixmap(money_xpm);
  178.   City::hShield = screen->CompilePixmap(shield_xpm);
  179.   City::hTrade = screen->CompilePixmap(trade_xpm);
  180.   City::hWorker = screen->CompilePixmap(worker_xpm);
  181. }
  182.  
  183. // find an empty spot and put work on it
  184. void City::PutExtraWorker()
  185. {
  186.   int mx, my, f = -1, p = -1, t = -1;
  187.   for (int xi = 0; xi < 5; ++xi) for (int yi = 0; yi < 5; ++yi) {
  188.     if ((xi % 4) == 0 && (yi % 4) == 0) continue;
  189.     int x1 = world->FixX(x+xi-2), y1 = world->FixY(y+yi-2);
  190.     if (world->RealVisible(x1, y1) == 0) continue;
  191.     if (world->Working(x1, y1) != 0) continue;
  192.     int cf = world->Food(x1, y1, ownerId);
  193.     int cp = world->Prod(x1, y1, ownerId);
  194.     int ct = world->Trade(x1, y1, ownerId);
  195.     if (cf > f || (cf == f && (ct > t || (ct == t && cp > p)))) {
  196.       f = cf; p = cp; t = ct;
  197.       mx = x1; my = y1;
  198.     }
  199.   }
  200.   if (f != -1)
  201.     world->Working(mx, my) = id;
  202. }
  203.  
  204. // find a spot and pollute it
  205. void City::PutPollution()
  206. {
  207.   int f = 0;
  208.   // first check that there is a spot not already polluted
  209.   for (int xi = 0; xi < 5; ++xi) for (int yi = 0; yi < 5; ++yi) {
  210.     if ((xi % 4) == 0 && (yi % 4) == 0) continue;
  211.     int x1 = world->FixX(x+xi-2), y1 = world->FixY(y+yi-2);
  212.     if ((x1 != x || y1 != y) && !world->Polluted(x1, y1)) {
  213.       f = 1;
  214.     }
  215.   }
  216.   // if there are any spots unpolluted, roll randomly until we pick one
  217.   while (f) {
  218.     int xi, yi;
  219.     xi = random() % 5;
  220.     yi = random() % 5;
  221.     if ((xi % 4) == 0 && (yi % 4) == 0) continue;
  222.     int x1 = world->FixX(x+(xi)-2);
  223.     int y1 = world->FixX(y+(yi)-2);
  224.     if ((x1 != x || y1 != y) && !world->Polluted(x1,y1)) {
  225.       world->Pollute(x1,y1);
  226.       f = 0;
  227.     }
  228.   }
  229. }
  230.  
  231. void City::ComputeEcon()
  232. {
  233.   Debug('c', "Computing economy for city %s\n", name);
  234.   prod = food = trade = pollution = working = 0;
  235.   for (int xi = 0; xi < 5; ++xi) for (int yi = 0; yi < 5; ++yi) {
  236.     if ((xi % 4) == 0 && (yi % 4) == 0) continue;
  237.     int x1 = world->FixX(x+xi-2), y1 = world->FixY(y+yi-2);
  238.     if (world->Working(x1, y1) != id) continue;
  239.     ulong enemyUnit = 0;
  240.     for (Lister<ulong> unitl = world->Units(x1, y1); unitl; unitl.Next())
  241.       if (trans->TransUnit(unitl.Elem())->ownerId != ownerId)
  242.     enemyUnit = unitl.Elem();
  243.     if (enemyUnit != 0) {
  244.       world->Working(x1, y1) = 0; // can't work here
  245.       continue;
  246.     }
  247.     if (working == size && !(x1 == x && y1 == y)) {
  248.       world->Working(x1, y1) = 0;
  249.       continue;
  250.     }
  251.     food += world->Food(x1, y1, ownerId);
  252.     prod += world->Prod(x1, y1, ownerId);
  253.     int val = world->Trade(x1, y1, ownerId);
  254.     if (val > 0) val += players[ownerId]->tradeBonus;
  255.     trade += val;
  256.     if (!(x1 == x && y1 == y)) ++working;
  257.   }
  258.   Player *p = players[ownerId];
  259.   elvi = size-working;
  260.   money = trade*p->tax/100;
  261.   luxuries = trade*p->luxuries/100;
  262.   science = trade-money-luxuries;
  263.   luxuries += 2*elvi;
  264.   goondas = size - FREE_CONTENTED;
  265.   if (goondas < 0) goondas = 0;
  266.   happy = 0;
  267.   food -= 2*size;
  268.   food -= foodSupport;
  269.   p->WonderEffect(this);
  270.   if (p->govt < REPUBLIC) // martial order
  271.     goondas -= world->Units(x, y).Count();
  272.   int transit=0, factory=0, mfg=0, plant=0, recycling=0, dirtyplant= 0;
  273.   for (Lister<charp> l = buildings; l; l.Next()) {
  274.     BuildObject *obj = buildObjects.Find(StrKey(l.Elem()));
  275.     switch (obj->type) {
  276.     case BANK:
  277.       luxuries = int(luxuries*1.5);
  278.       money = int(money*1.5);
  279.       break;
  280.     case CATHEDRAL:
  281.       goondas -= p->cathedralEffect;
  282.       break;
  283.     case COLOSSEUM:
  284.       goondas -= 3;
  285.       break;
  286.     case POWERPLANT:
  287.       dirtyplant = 1;
  288.       // no break
  289.     case HYDROPLANT:
  290.       // no break
  291.     case FUSIONPLANT:
  292.       // no break
  293.     case NUCLEARPLANT:
  294.       plant = 1;
  295.       break;
  296.     case FACTORY:
  297.       factory = 1;
  298.       break;
  299.     case MFGPLANT:
  300.       mfg = 1;
  301.       break;
  302.     case LIBRARY:
  303.       science = int(science*1.5);
  304.       break;
  305.     case MARKETPLACE:
  306.       luxuries = int(luxuries*1.5);
  307.       money = int(money*1.5);
  308.       break;
  309.     case RECYCLINGCTR:
  310.      recycling = 1;
  311.      break;
  312.     case TEMPLE:
  313.       goondas = goondas-p->templeEffect;
  314.       break;
  315.     case UNIVERSITY:
  316.       if (p->HasWonder("Isaac Newton's College"))
  317.         science = science*2;
  318.       else
  319.         science = int(science*1.5);
  320.     }
  321.   }
  322.   if (p->HasWonder("Hoover Dam")) {
  323.     plant = 1;
  324.   }
  325.   if (mfg) {
  326.     prod = prod*2;
  327.   }
  328.   else if (factory) {
  329.     prod = int(prod*1.5);
  330.   }
  331.   if (plant && (mfg || factory)) {
  332.     prod = int(prod*1.5);
  333.   }
  334.   if (!transit) {
  335.     if (Discovered(ownerId,"Automobile")) {
  336.       pollution += size;
  337.     }
  338.     else {
  339.       if (size > 10) {
  340.         pollution += size - 10;
  341.       }
  342.     }
  343.   }
  344.   if (dirtyplant) {
  345.     if (recycling) {
  346.       pollution += prod/2;
  347.     }
  348.     else {
  349.       pollution += prod;
  350.     }
  351.   }
  352.  
  353. // SETI program
  354.   if (p->HasWonder("SETI Program"))
  355.     science = int(science*1.5);
  356.  
  357. // each 2 luxuries reduces goondas by one,
  358. // when run out of goondas actually create happy instead:
  359.   if (goondas<0) goondas = 0;
  360.   goondas -= int(luxuries/2);
  361.   if (goondas < 0) {
  362.      happy-= goondas;
  363.      goondas = 0;
  364.   }
  365.  
  366. // prod, money and science zeroed if goondas > happy:
  367.   if (goondas > happy) {
  368.     prod = 0;
  369.     money = 0;
  370.     science = 0;
  371.   }
  372.   int support = units.Count();
  373.   if (p->govt <= DESPOT) support -= size;
  374.   if (support < 0) support = 0;
  375.   surpProd = prod-support;
  376. }
  377.  
  378. int City::Update()
  379. {
  380.   // size zero = city dies:
  381.   if (size <= 0) return 0;
  382.   ComputeEcon();
  383.   Debug('c', "Updating city %s\n", name);
  384.   while (surpProd < 0) { // kill a unit
  385.     Unit *unit = trans->TransUnit(units.RemoveHead());
  386.     AddMessage(messages, "%s can't support %s", name, names[unit->Type()]);
  387.     *moveQ << PieceMove(unit->id, PIECE_DIE, 0, 0);
  388.     if (unit->type == SETTLER)
  389.       ++food;
  390.     delete unit;
  391.     ++surpProd;
  392.   }
  393.   accProd += surpProd;
  394.   foodStored += food;
  395.   if (foodStored < 0) { // kill off settlers
  396.     for (Lister<ulong> unitl = units; unitl && foodStored < 0;) {
  397.       Unit *unit = trans->TransUnit(unitl.Elem());
  398.       if (unit->type == SETTLER) {
  399.     AddMessage(messages, "%s can't feed Settlers", name);
  400.     *moveQ << PieceMove(unit->id, PIECE_DIE, 0, 0);
  401.     delete unit;
  402.     unitl = units;
  403.     ++foodStored;
  404.       }
  405.       else
  406.     unitl.Next();
  407.     }
  408.   }
  409.   if (foodStored < 0) {
  410.     foodStored = 0;
  411.     --size;
  412.     foodStoreCap -= 10;
  413.     AddMessage(messages, "Famine in %s, population decrease", name);
  414.     if (size <= 0) // city dies
  415.       return 0;
  416.     else
  417.       *moveQ << PieceMove(id, CITY_SIZE, size, 0);
  418.     DrawIfVisible();
  419.   }
  420.   if (foodStored >= foodStoreCap) { // eat food, maybe grow city
  421.     // check for granary to retain half of foodStored:
  422.     if (HasBuilding("Granary")) foodStored -= (int) foodStoreCap/2;
  423.     else foodStored -= foodStoreCap;
  424.     // cannot pass size 10 without aquaduct
  425.     if ((size >= 10) && !HasBuilding("Aquaduct")) {
  426.       AddMessage(messages, "%s needs aquaduct to grow", name);
  427.     } else {
  428.       ++size;
  429.       *moveQ << PieceMove(id, CITY_SIZE, size, 0);
  430.       foodStoreCap += 10;
  431.       PutExtraWorker();
  432.       DrawIfVisible();
  433.     }
  434.   }
  435.   if (building != NULL && accProd >= needProd) {
  436.     Produce();
  437.   }
  438.   ComputeEcon();
  439.   if (random() % 100 +1 <= pollution) {
  440.     PutPollution();
  441.   }
  442. // notification of civil disorder:
  443. // also, collapse of democracy into anarchy if any city does civil-disorder.
  444.   if (goondas > happy) {
  445.      AddMessage(messages, "Civil disorder in %s", name);
  446.      if (players[ownerId]->govt == DEMOCRACY)
  447.     players[ownerId]->govt = ANARCHY;
  448.   }
  449.   players[ownerId]->money += money;
  450.   players[ownerId]->scienceAcc += science;
  451.   for (Lister<charp> l = buildings; l;) {
  452.     BuildObject *obj = buildObjects.Find(StrKey(l.Elem()));
  453.     players[ownerId]->money -= obj->maint;
  454.     if (players[ownerId]->money < 0) {
  455.       players[ownerId]->money += obj->prodCost;
  456.       AddMessage(messages, "%s can't maintain %s", name, l.Elem());
  457.       l.Delete();
  458.       if (strcmp(obj->name,"City Walls")==0)
  459.         *moveQ << PieceMove(id,CITY_WALL,0,0);
  460.       else players[ownerId]->DeleteWonder(obj->name);
  461.     }
  462.     else
  463.       l.Next();
  464.   }
  465.   Debug('c', "Done updating city %s\n", name);
  466.   return 1;
  467. }
  468.